feat: add DocumentProgressCard component#338
Conversation
A presentational card for a transactional document's fulfilment state:
a derived completion percentage, a stacked progress bar, and a
received / returned / yet-to-receive legend. Ported from the omakase
FulfilmentRate component as a view-only, props-driven component — no
data-fetching or domain logic.
Each bucket takes { value, label?, color? } with per-bucket defaults.
Percent is derived as received / (received + yetToReceive); pass
returnedCountsAsComplete={false} to subtract returns from progress.
Includes behavioral + snapshot tests, docs, catalogue entry, and a changeset.
Refs tailor-inc/platform-planning#784
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a /dashboard/document-progress example page demonstrating the component's states (empty, partial, returns-subtracted, complete, relabelled/custom-colors, returns-heavy) and a sidebar nav link. Refs tailor-inc/platform-planning#784 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adversarial review surfaced two ways the bar could contradict the
header percentage:
- With returnedCountsAsComplete={false}, the header showed
(received − returned)/total but the bar still filled received/total.
The returned segment is now part of the colored fill only when it
counts as complete, so the fill always equals the header percentage.
- When returned > received, the net-received clamp left a returned
segment wider than the header implied. returned is now clamped to
received before deriving the breakdown.
Also sanitize incoming amounts (non-finite/negative → 0) so invalid
input can't render NaN/negative figures in the legend or break the bar.
Adds tests for both consistency cases and sanitization; updates docs
and the affected snapshot.
Refs tailor-inc/platform-planning#784
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Treat the bar as a composition rather than a pure progress fill: it
always renders a net-received segment (received − returned) followed by
a returned segment, in both modes. The header percentage maps onto the
net-received segment, plus the returned segment when
returnedCountsAsComplete is true.
This keeps returned items visible in the bar even when they're excluded
from the rate (returnedCountsAsComplete={false}), matching the Figma /
omakase intent, while the net-received segment still equals the header.
Retains the returned≤received clamp and input sanitization. Updates the
affected test, snapshot, and docs.
Refs tailor-inc/platform-planning#784
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/review |
|
✅ API Design Review completed successfully! API Design Review complete for PR #338 (DocumentProgressCard). Found 2 Low-severity issues (missing public exports for DocumentProgressItem/DocumentProgressColor; incomplete JSDoc on returnedCountsAsComplete). No High or Medium issues. Verdict: Approve. |
There was a problem hiding this comment.
Generated by API Design Review for issue #338 · 366.7 AIC · ⌖ 12.5 AIC · ⊞ 26.3K
Comment /review to run again
…t-progress-card # Conflicts: # examples/vite-app/src/App.tsx
|
I’m curious how you’re thinking about the long-term scope of this API. Right now the public shape is fixed to three buckets — Is the intent for this component to stay specific to that receiving/returning model, or do you expect it to be reused for other document workflows as well? In ERP-style flows, it feels fairly common to need an additional status such as If broader stretch is not really a goal here, I wonder whether we should at least make the three fixed buckets more generic at the API level as well, instead of baking the receiving-specific terminology into the public props. For example: type DocumentProgressValue = {
value: number;
label: string;
color?: DocumentProgressColor;
};
type DocumentProgressCardProps = {
title?: string;
values: {
complete: DocumentProgressValue;
subtract: DocumentProgressValue;
remaining: DocumentProgressValue;
};
className?: string;
};That would still preserve the current three-slot model and progress calculation, but make the component easier to reuse across adjacent workflows. Happy to hear your thought if we are on this direction. |
… wrapper Per PR review: make DocumentProgressCard generic so it stretches to any document workflow (shipped/cancelled/backordered/…), and move the receiving-specific logic into a thin opinionated wrapper. - DocumentProgressCard: now takes arbitrary `segments` + an explicit `percent`, with an optional `legend` override and `total` (for an unfilled track remainder). No baked-in domain terminology. - ProcurementFulfilmentProgressCard: new wrapper keeping the existing received/returned/yetToReceive API and business logic (derived percentage, returned≤received clamp, net-received/returned bar decomposition, sanitization) — composed over the generic card. The legend still shows the full received amount while the bar decomposes. Both are exported. Updates tests (generic + wrapper), snapshots, docs (two pages), catalogue entries, the changeset, and the showcase page. Addresses #338 (comment) Refs tailor-inc/platform-planning#784 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per review follow-up: - DocumentProgressCard: `color` is now required on each segment — drop the by-position default palette so colors are always explicit. - ProcurementFulfilmentProgressCard: correct the domain wording from "goods receipt" to "purchase order" across the component doc comment, docs, catalogue, changeset, and example (it tracks PO fulfilment). Updates tests and docs accordingly. Refs tailor-inc/platform-planning#784 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
I’m strongly against including ProcurementFulfilmentProgressCard in AppShell.
This component is not just “opinionated”; it is explicitly domain-specific. Its API encodes procurement concepts directly (received, returned, yetToReceive), so adding it to AppShell would mean expanding the core library into business-domain UI. Once we allow that, it becomes much easier for more domain-specific components to accumulate in AppShell over time.
I think the right boundary is:
- AppShell should provide the generic primitive, such as
DocumentProgressCard - Domain-specific components should live outside AppShell
In particular, I think this kind of component belongs in erp-kit, and more specifically should be part of a module frontend template there, rather than part of AppShell core. That keeps AppShell focused on reusable platform-level building blocks, while erp-kit owns the ERP/domain layer.
If we have a real cross-domain need, we should extract the generic abstraction into AppShell — not promote a procurement-specific wrapper into core.
There was a problem hiding this comment.
I hear what you're saying and I agree but have strange feeling. I am a little unclear on the overall strategy on domain-opinionated components. We want some consistency for something like Purchase Order (which you are saying should live in erp-kit... I understand why). If components in erp-kit is an established strategy, perhaps we need an announcement and education on it. Though something feels odd to me and counterproductive having component definitions in different places (erp-kit vs app-shell), and sometimes having a library of components and sometimes having templates, it's all a bit confusing and lacking guidance. I will work on some draft definitions to help me understand and we can reason about it as a group.
I will remove the ProcurementFulfilmentProgressCard from here, to align with AppShell being domain-agnostic, but in the patterns I will create a Purchase Order example for the DocumentProgressCard implementation. At least then there's something not strict that can be used as a guide in basically what is the place this component will be used most
There was a problem hiding this comment.
I agree with your point here. The boundary between AppShell, erp-kit, and templates still feels underdefined.
If we want to keep building domain-specific components like ProcurementFulfilmentProgressCard, I think we also need to move the erp-kit frontend-template initiative forward in parallel. Otherwise, we end up agreeing that they should not live in AppShell, but still do not have a clear first-party place for them to go.
So this feels like a good signal that we should invest in the erp-kit side soon.
Team feedback: the wrapper was too domain-specific. Remove it and keep only the generic DocumentProgressCard. The purchase-order fulfilment use case now lives as guidance rather than a component: the docs gain a complete, copyable "purchase-order fulfilment" recipe (labels, indigo/pink/neutral colors, net-received + returned bar split, legend override, derived percentage with the returned-counts-as-complete toggle), and the example app demonstrates the same pattern via a small local PurchaseOrderFulfilment helper. Removes the component, its tests, snapshot, docs page, catalogue entry, and public export. Updates the changeset. Refs tailor-inc/platform-planning#784 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Adds
DocumentProgressCard— a presentational card for a transactional document's fulfilment / lifecycle state: a derived completion percentage, a stacked progress bar, and a received / returned / yet-to-receive legend. Ported from the omakaseFulfilmentRatecomponent as a view-only, props-driven component (no data-fetching or domain logic).Implements tailor-inc/platform-planning#784 (Document Progress Card), AppShell v1.6.0. Matches the Figma design.
Screenshot
API
received/returned/yetToReceive— each{ value, label?, color? }with per-bucket label/color defaults (indigo / pink / neutral).received / (received + yetToReceive).returnedis a subset ofreceived, so it doesn't change the denominator. PassreturnedCountsAsComplete={false}to subtract returns from progress.titledefaults to"Fulfilment rate". Curated color palette (indigo/pink/green/amber/red/blue/neutral).Changes
packages/core/src/components/document-progress-card/— component (Pattern C),types.ts,index.ts, testspackages/core/src/index.ts(minimal surface: component + Props type)docs/components/document-progress-card.md, catalogue entry, changeset (minor)examples/vite-app—/dashboard/document-progressshowcase page + sidebar linkTesting
🤖 Generated with Claude Code